`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: CBICR, Tsinghua Univ.
// Engineer: Hongyi Li
// 
// Create Date: 2024/12/23 11:57:37
// Design Name: 
// Module Name: Router
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module NaiveRouter
#(
    parameter DataWidth = 'd32,
    parameter FifoDepth = 'd4,
    parameter VCNumber  = 'd4
)(
    input                                       clk, rst_n,
    input   [DataWidth-1:0]                     i_data_e, i_data_w, i_data_n, i_data_s, i_data_l,
    input   [VCNumber*$clog2(FifoDepth)-1:0]    i_credit_e, i_credit_w, i_credit_n, i_credit_s, i_credit_l, 
    output  [DataWidth-1:0]                     o_data_e, o_data_w, o_data_n, o_data_s, o_data_l,
    output  [VCNumber*$clog2(FifoDepth)-1:0]    o_credit_e, o_credit_w, o_credit_n, o_credit_s, o_credit_l
);

wire [DataWidth                    -1:0]      i_data_array     [0:4];
wire [VCNumber                     -1:0]      i_available_vc   [0:4];

wire [VCNumber * DataWidth         -1:0]      bw_data          [0:4];
wire [VCNumber * 1                 -1:0]      bw_valid         [0:4];
wire [VCNumber * $clog2(FifoDepth) -1:0]      bw_credits       [0:4];
wire [VCNumber * 5                 -1:0]      bw_dirs          [0:4];
wire [VCNumber * DataWidth         -1:0]      bw_heads         [0:4];

wire [VCNumber * 5                 -1:0]      va_next_vc       [0:4];
wire [VCNumber * 5                 -1:0]      va_winner;
wire [                             24:0]      va_mat;

wire [                             24:0]      sa_mat;
wire [DataWidth                    -1:0]      vmux_data_array  [0:4];

assign i_data_array[0] = i_data_e;
assign i_data_array[1] = i_data_w;
assign i_data_array[2] = i_data_n;
assign i_data_array[3] = i_data_s;
assign i_data_array[4] = i_data_l;

assign o_credit_e = bw_credits[0];
assign o_credit_w = bw_credits[1];
assign o_credit_n = bw_credits[2];
assign o_credit_s = bw_credits[3];
assign o_credit_l = bw_credits[4];

// i: direction-ewnsl
// j: virtual-channel
genvar i, j, k; 
generate
    for (j = 0; j < VCNumber; j = j + 1) begin
        assign i_available_vc[0][j] = ~(|i_credit_e[$clog2(FifoDepth)*(j+1)-1:$clog2(FifoDepth)*j]);
        assign i_available_vc[1][j] = ~(|i_credit_w[$clog2(FifoDepth)*(j+1)-1:$clog2(FifoDepth)*j]);
        assign i_available_vc[2][j] = ~(|i_credit_n[$clog2(FifoDepth)*(j+1)-1:$clog2(FifoDepth)*j]);
        assign i_available_vc[3][j] = ~(|i_credit_s[$clog2(FifoDepth)*(j+1)-1:$clog2(FifoDepth)*j]);
        assign i_available_vc[4][j] = ~(|i_credit_l[$clog2(FifoDepth)*(j+1)-1:$clog2(FifoDepth)*j]);
    end
endgenerate

generate
    // Five Directions
    for (i = 0; i < 5; i = i + 1) begin

        // BW: Buffer Write (EWNSL)
        BufferWr #(
            .DataWidth(DataWidth), .FifoDepth(FifoDepth), .VCNumber(VCNumber)
        ) BW_U (
            .clk(clk), .rst_n(rst_n), .i_data(i_data_array[i]), .i_pop_reqs(0), 
            .o_data(bw_data[i]), .o_valid(bw_valid[i]), .o_credits(bw_credits[i]), 
            .o_head(bw_heads[i]), .o_directions(bw_dirs[i])
        );
        
        // RC: Routing Compute
        for (j = 0; j < VCNumber; j = j + 1) begin
            RtrComp RC_U (
                .clk       ( clk ),
                .rst_n     ( rst_n ),
                .i_valid   ( | bw_heads[i][(DataWidth*j+VCNumber+19) : (DataWidth*j+20)] ),
                .i_core_id ( 6'd1 ), // For Evaluation
                .i_dst     ( bw_heads[i][(DataWidth*j+19) : (DataWidth*j+14)] ),
                .o_dir     ( bw_dirs[i][(5*j+4) : (5*j)] )
            );
        end
    end
endgenerate

// VA: Virtual-Channel Allocation
VcAlloc #( 
    .DataWidth ( DataWidth ),
    .FifoDepth ( FifoDepth ),
    .VCNumber  ( VCNumber )
) VA_U (
    .clk       ( clk ), 
    .rst_n     ( rst_n ), 
    .i_credit  ( {i_available_vc[4], i_available_vc[3], i_available_vc[2], i_available_vc[1], i_available_vc[0]} ),
    .i_dir     ( {       bw_dirs[4],        bw_dirs[3],        bw_dirs[2],        bw_dirs[1],        bw_dirs[0]} ),
    .o_next_vc ( {    va_next_vc[4],     va_next_vc[3],     va_next_vc[2],     va_next_vc[1],     va_next_vc[0]} ),
    .o_winner  ( va_winner ),
    .o_adj_mat ( va_mat )
);

// SA: Switch Allocation (VC -> 1 Out)
SwAlloc SA_U (
    .clk          ( clk ), 
    .rst_n        ( rst_n ),
    .i_adj_mat    ( va_mat ),
    .o_alloc_mat  ( sa_mat )
);

VcMux #(
    .DataWidth    ( DataWidth ),
    .VCNumber     ( VCNumber )
) VMUX_U (
    .i_data_e     ( bw_data[0] ),
    .i_data_w     ( bw_data[1] ),
    .i_data_n     ( bw_data[2] ),
    .i_data_s     ( bw_data[3] ),
    .i_data_l     ( bw_data[4] ),
    .i_winner     ( va_winner ),
    .o_data_e     ( vmux_data_array[0] ),
    .o_data_w     ( vmux_data_array[1] ),
    .o_data_n     ( vmux_data_array[2] ),
    .o_data_s     ( vmux_data_array[3] ),
    .o_data_l     ( vmux_data_array[4] )
);

// ST: Switch Traveral (P x P)
SwTrav 
# (
    .DataWidth    ( DataWidth ),
    .VCNumber     ( VCNumber )
) ST_U (
    .clk          ( clk ), 
    .rst_n        ( rst_n ),
    .i_alloc_mat  ( sa_mat ),
    .i_data_e     ( vmux_data_array[0] ), 
    .i_data_w     ( vmux_data_array[1] ), 
    .i_data_n     ( vmux_data_array[2] ), 
    .i_data_s     ( vmux_data_array[3] ), 
    .i_data_l     ( vmux_data_array[4] ),
    .o_data_e     ( o_data_e ),
    .o_data_w     ( o_data_w ),
    .o_data_n     ( o_data_n ),
    .o_data_s     ( o_data_s ),
    .o_data_l     ( o_data_l )
);

endmodule
